<?php
namespace App\Common\Exception;

use App\Common\Exception\Handler\ApiResponseExceptionHandler;
use App\Common\Exception\Handler\RouteNotFoundExceptionHandler;
use App\Common\Exception\Handler\ValidateExceptionHandler;
use App\Common\Util\AdminAuth;
use App\Common\Util\ApiAuth;
use App\Common\Util\TP;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\RouteNotFoundException;
use think\exception\ValidateException;
use think\Request;
use think\Response;
use Throwable;

/**
 * 应用异常处理类
 */
class ExceptionHandle extends Handle
{
    /**
     * 不需要记录信息（日志）的异常类列表
     * @var array
     */
    protected $ignoreReport = [
        HttpException::class,
        HttpResponseException::class,
        ModelNotFoundException::class,
        DataNotFoundException::class,
        ValidateException::class,
        ApiMessageException::class,
        ApiDataException::class,
    ];

    /**
     * 自定义异常处理，优先级依次往下
     *
     * @var array|string[]
     */
    protected array $handlers = [
        ApiMessageException::class    => ApiResponseExceptionHandler::class,
        ApiDataException::class       => ApiResponseExceptionHandler::class,
        RouteNotFoundException::class => RouteNotFoundExceptionHandler::class,
        ValidateException::class      => ValidateExceptionHandler::class,
    ];

    /**
     * 记录异常信息（包括日志或者其它方式记录）
     *
     * @access public
     * @param  Throwable $exception
     * @return void
     */
    public function report(Throwable $exception): void
    {
        // 使用内置的方式记录异常日志
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @access public
     * @param Request $request
     * @param Throwable $e
     * @return Response
     */
    public function render(Request $request, Throwable $e): Response
    {
        // 添加自定义异常处理机制
        if ($handle = $this->getHandler($e)) {
            $res = $handle->handle();

            if ($handle->isStop()) {
                return $res;
            }
        }
        try {
            op_log(
                des: '系统异常,IP:' . $request->ip(),
                data: array_merge(TP::request()->param(), $_FILES ? ['_FILE' => $_FILES] : []),
                exception: ["message" => $e->getMessage(), "trace" => $e->getTraceAsString()],
                userInfo: AdminAuth::getInfo() ?: ApiAuth::get(),
            );
        } catch (\Throwable $throwable) {}
        // 其他错误交给系统处理
        return parent::render($request, $e);
    }

    /**
     * @param Throwable $throwable
     * @return ExceptionHandlerInterface|null
     */
    private function getHandler(\Throwable $throwable): ?ExceptionHandlerInterface
    {
        foreach ($this->handlers as $exception => $handler) {
            if ($throwable instanceof $exception) {
                return new $handler($throwable);
            }
        }

        return null;
    }
}
